import { deepClone, isBoolean, isNullOrUndefined } from '../../utils';
import {
  TABLE_ACTION_CELL,
  TABLE_INDEX,
  TABLE_SELECT_CELL,
  defaultColumnConfig,
} from '../config';
import { getElementSize } from '../utils';

import type { ColumnDef } from '@tanstack/react-table';
import type { SettingCheckBoxInfo } from '../components/DisplayTable';
import type { ScrollInfo } from '../context/table';
import type {
  AnyObject,
  Column,
  ColumnDataKeyInfo,
  ColumnFormulaInfo,
  ColumnMeta,
  ColumnPinning,
  ColumnPinningInfo,
  ColumnSizes,
  Rules,
} from '../type';
/*
export function getAllFlatColumns<T extends AnyObject>(columns: T[], result: T[] = [], parent?: T) {
  columns.forEach((column) => {
    const columnMeta = (column.columnDef && column.columnDef.meta) || column.meta || {};
    const {columns: subColumns, ...meta} = columnMeta;
    const data = {...column, meta};
    if (parent) data.meta.parent = parent;
    result.push(data);

    if (subColumns && subColumns.length) {
      getAllFlatColumns(subColumns, result, data);
    }
  });
  return result;
}
*/
export function getAllFlatColumns<T extends AnyObject>(
  columns: T[],
  result: T[] = [],
  parent?: T,
) {
  columns.forEach((column) => {
    const columnMeta =
      (column.columnDef && column.columnDef.meta) || column.meta || {};
    const { columns: subColumns, ...meta } = columnMeta;
    const data = { ...column, meta: { ...meta, columns: subColumns } };
    if (parent) data.meta.parent = parent;
    result.push(data);

    if (subColumns && subColumns.length) {
      getAllFlatColumns(subColumns, result, column);
    }
  });
  return result;
}

export function getColumn<T extends AnyObject>(id: string, columns: T[]) {
  for (let column of columns) {
    if (column.id === id) {
      return column;
    }
    const columnMeta =
      (column.columnDef && column.columnDef.meta) || column.meta || {};

    if (columnMeta.columns && columnMeta.columns) {
      getColumn(id, columnMeta.columns);
    }
  }
}

// 初始化表格列，处理表格子列id
export function initColumns<T extends AnyObject>(columns: Column<T>[] = []) {
  const deep = (
    columns: Column<T>[],
    result: Column<T>[] = [],
    parent?: Column<T>,
  ) => {
    columns.forEach((data) => {
      const { meta = {}, ...item } = data;
      const { columns: subColumns, ...columnMeta } = meta;
      const column: Column<T> = {
        ...item,
        meta: { ...columnMeta, dataKey: columnMeta.dataKey || item.id },
      };
      // @ts-ignore
      if (!column.accessorKey)
        // @ts-ignore
        column.accessorKey = columnMeta.dataKey || column.id;

      if (parent) {
        Object.assign(column, {
          id: `${parent.id}_${column.id}`,
          meta: {
            ...columnMeta,
            dataKey: columnMeta.dataKey || column.id,
            columns: [],
          },
        });
      }

      if (subColumns && subColumns.length) {
        if (!column.meta.columns) column.meta.columns = [];
        column.meta.columns = deep(subColumns, column.meta.columns, data);
      }
      result.push(column);
    });
    return result;
  };

  return deep(columns);
}
// 初始化列顺序
export function initColumnOrder<T extends AnyObject>(
  columns: Column<T>[],
  isDeep?: boolean,
) {
  const deep = (columns: Column<T>[], result: string[] = []) => {
    columns.forEach((column) => {
      const { id, meta = {} } = column;
      const { columns: subColumns } = meta;
      result.push(id);
      if (isDeep && subColumns && subColumns.length) {
        deep(subColumns, result);
      }
    });
    return result;
  };
  return deep(columns);
}
// 初始化列显示
export function initColumnVisibility<T extends AnyObject>(
  columns: Column<T>[],
  isDeep?: boolean,
) {
  const deep = (columns: Column<T>[], result: SettingCheckBoxInfo = {}) => {
    columns.forEach((column) => {
      const { id, meta = {} } = column;
      const { columns: subColumns } = meta;
      result[id] = !meta.parent;

      if (isDeep && subColumns && subColumns.length) {
        deep(subColumns, result);
      }
    });
    return result;
  };

  return deep(columns);
}
export function initColumnSummary<T extends AnyObject>(
  columns: Column<T>[],
  columnVisibility: SettingCheckBoxInfo,
) {
  const result: SettingCheckBoxInfo = {};
  columns.forEach((column) => {
    const columnMeta = column.meta || {};
    const { dataKey } = columnMeta;
    const meta =
      ((dataKey && getColumn(dataKey, columns)) || column).meta || {};
    const { editType, dataType } = meta;
    const isCanSummary = (editType || dataType) === 'number';
    const isShow = !!columnVisibility[column.id];
    // 列显示，且是数值类型的列才可使用合计
    result[column.id] = isShow && isCanSummary;
  });
  return result;
}
export function getVisibleFlatColumns<T extends AnyObject>(
  columns: ColumnDef<T>[],
  columnVisibility: SettingCheckBoxInfo,
) {
  return getAllFlatColumns(columns).filter(
    (column) => columnVisibility[column.id],
  );
}
// 调整显示的列按固定列排序
export function adjustColumnOrder<T extends AnyObject>(
  columns: T[],
  columnPinning: ColumnPinning,
) {
  const result: T[] = [];
  // 防止columnPinning的id，columns里没有该列
  const updateColumnPinnning = (
    columnPinning: ColumnPinning,
    columnIds: string[],
  ) => {
    const { left, right } = columnPinning;
    const update = (pinning: string[]) => {
      return pinning.filter((id) => columnIds.includes(id));
    };
    const result: ColumnPinning = { left: update(left), right: update(right) };

    return result;
  };
  const columnIds: string[] = columns.map((column) => column.id);
  const { left, right } = updateColumnPinnning(columnPinning, columnIds);

  // 非固定列插入位置
  let columnIndex = left.length;

  columns.forEach((column) => {
    const { id } = column;
    const leftIndex = left.indexOf(id);
    const rightIndex = right.indexOf(id);
    if (leftIndex > -1) {
      result[leftIndex] = column;
    } else if (rightIndex > -1) {
      result[columns.length - right.length + rightIndex] = column;
    } else {
      result[columnIndex] = column;
      columnIndex += 1;
    }
  });
  return result;
}

// 列固定
export function getColumnPinning<T extends AnyObject>(
  columns: ColumnDef<T>[],
): ColumnPinning {
  const tableColumns = getAllFlatColumns(columns);

  // 选择，索引左固定，操作右固定
  const getDefaultPinning = (pinning: string[], columns: ColumnDef<T>[]) => {
    const columnIds = columns.map((column) => column.id);
    return pinning.filter((item) => columnIds.includes(item));
  };
  const defaultLeftPinning = [TABLE_SELECT_CELL, TABLE_INDEX];
  const defaultRightPinning = [TABLE_ACTION_CELL];
  const left = getDefaultPinning(defaultLeftPinning, tableColumns);
  const right = getDefaultPinning(defaultRightPinning, tableColumns);

  const columnPinning = { left, right };
  tableColumns.forEach((column) => {
    const { fixed } = (column.meta as ColumnMeta<T>) || {};
    if (fixed) {
      const align = isBoolean(fixed) ? 'left' : fixed;
      align === 'left'
        ? columnPinning[align].push(column.id)
        : columnPinning[align].splice(
            right.length - defaultRightPinning.length,
            0,
            column.id,
          );
    }
  });
  return columnPinning;
}
export function setColumnPinningInfo<T extends AnyObject>(
  pinning: string[],
  columns: T[],
) {
  const pinningInfo: { [key in string]: number } = {};

  pinning.forEach((id, i) => {
    const column = getColumn(id, columns);
    if (column) {
      const isFirst = !Object.keys(pinningInfo).length;
      if (!isFirst && i > 0) {
        const prevId = pinning[i - 1];
        const preColumn = getColumn(prevId, columns);
        const columnMeta =
          (preColumn.columnDef && preColumn.columnDef.meta) ||
          (preColumn.meta as ColumnMeta<T>) ||
          {};
        const preWidth =
          columnMeta.width ||
          getElementSize(document.getElementById(prevId), 'width');
        pinningInfo[id] = pinningInfo[prevId] + preWidth;
      } else {
        pinningInfo[id] = 0;
      }
    }
  });
  return pinningInfo;
}
export function getColumnPinningInfo<T extends AnyObject>(
  columnPinning: ColumnPinning,
  columns: T[],
): ColumnPinningInfo {
  const { left, right } = columnPinning;
  return {
    left: setColumnPinningInfo(left, columns),
    right: setColumnPinningInfo(right.map((item) => item).reverse(), columns),
  };
}
export function setColumnPinningWidth<T extends AnyObject>(
  pinning: string[],
  columns: T[],
) {
  let width = 0;
  pinning.forEach((id) => {
    const column = getColumn(id, columns);
    if (column) {
      const columnMeta =
        (column.columnDef && column.columnDef.meta) ||
        (column.meta as ColumnMeta<T>) ||
        {};
      const columnWidth =
        columnMeta.width ||
        getElementSize(document.getElementById(id), 'width');
      width += columnWidth;
    }
  });
  return width;
}
// 获取左右固定列的宽度
export function getColumnPinningWidth<T extends AnyObject>(
  columnPinning: ColumnPinning,
  columns: T[],
) {
  const { left, right } = columnPinning;
  return {
    left: setColumnPinningWidth(left, columns),
    right: setColumnPinningWidth(right.map((item) => item).reverse(), columns),
  };
}
export function getColumnPinningDom(columnPinning: ColumnPinning) {
  const { left, right } = columnPinning;
  const getDom = (arr: string[]) =>
    arr.map((id) => document.getElementById(id));
  return getDom(left).concat(getDom(right));
}
// 获取列的总宽度
export function getColumnTotalSize<T extends AnyObject>(columns: T[]) {
  let width = 0;
  columns.forEach((column) => {
    const { id } = column;
    const columnMeta =
      (column.columnDef && column.columnDef.meta) ||
      (column.meta as ColumnMeta<T>) ||
      {};
    const columnWidth =
      columnMeta.width || getElementSize(document.getElementById(id), 'width');
    width += columnWidth;
  });
  return width;
}
/*
 * 当前列距表格左右边距距离思路
 * 1. 无左右滚动时，根据所有列的宽度计算当前列左右边距
 *
 * 2. 有滚动无固定列时
 *    左边距left = 步骤1 当前列左边距值 - scrollLeft
 *    右边距right = 步骤1 当前列右边距值 - ((scrollWidth - clientWidth)[可滚动的最大值] - scrollLeft)
 *
 * 3. 有滚动有固定列时（需过滤掉固定列 计算步骤1中的所有列的左右边距）
 *    3.1 当前列不是固定列
 *    左边距left = 步骤2 当前列左边距值 + 左侧固定列总宽度
 *    右边距right = 步骤2 当前列右边距值 + 右侧固定列总宽度
 *
 *    3.2 当前列是固定列 （需不过滤掉固定列 计算步骤1中的所有列的左右边距）
 *    3.2.1 左固定列
 *    左边距left = 步骤1 当前列的左边距
 *    右边距right = clientWidth - 当前列 下一个列的左边距
 *    3.2.2 右固定列
 *    左边距left = clientWidth - 当前列 上一个列的右边距
 *    右边距right = 步骤1 当前列的右边距
 */
export interface CaculateColumnMarginParams<T extends AnyObject> {
  columnId: string;
  scroll: ScrollInfo;
  columnPinning: ColumnPinning;
  visibleFlatColumns: Column<T>[];
}
export function caculateColumnMargin<T extends AnyObject>(
  params: CaculateColumnMarginParams<T>,
) {
  const { columnId, scroll, columnPinning, visibleFlatColumns } = params;
  const { scrollLeft, scrollWidth, clientWidth } = scroll;
  const isCanScroll = clientWidth < scrollWidth;
  const { left, right } = columnPinning;

  if (!columnId) return { left: 0, right: 0 };

  // 当前列是固定列时
  if (left.includes(columnId) || right.includes(columnId)) {
    const columnIds = visibleFlatColumns.map((column) => column.id);
    const allColumnPinningInfo = getColumnPinningInfo(
      { left: columnIds, right: columnIds },
      visibleFlatColumns,
    );
    const pinningAlign = left.includes(columnId) ? 'left' : 'right';
    const curIndex = columnIds.indexOf(columnId);

    return {
      left:
        pinningAlign === 'left'
          ? allColumnPinningInfo.left[columnId]
          : clientWidth - allColumnPinningInfo.right[columnIds[curIndex - 1]],
      right:
        pinningAlign === 'left'
          ? clientWidth - allColumnPinningInfo.left[columnIds[curIndex + 1]]
          : allColumnPinningInfo.right[columnId],
    };
  } else {
    const columns = visibleFlatColumns.filter(
      (column) =>
        !isCanScroll ||
        (!left.includes(column.id) && !right.includes(column.id)),
    );
    const columnIds = columns
      .filter(
        (column) => !left.includes(column.id) && !right.includes(column.id),
      )
      .map((column) => column.id);
    const info = getColumnPinningInfo(
      { left: columnIds, right: columnIds },
      columns,
    );
    const columnPinningWidth = getColumnPinningWidth(
      columnPinning,
      visibleFlatColumns,
    );

    return {
      left: info.left[columnId] - scrollLeft + columnPinningWidth.left,
      right:
        info.right[columnId] -
        (scrollWidth - clientWidth - scrollLeft) +
        columnPinningWidth.right,
    };
  }
}
// columnPinningInfo 转换成 columnPinning
export function tranformColumnPinning(columnPinningInfo: ColumnPinningInfo) {
  const { left, right } = columnPinningInfo;

  const getPinning = (
    pinning: { [key in string]: number },
    sortType: 'ascend' | 'descend' = 'ascend',
  ) => {
    const columnPinning: string[] = [];
    for (let key in pinning) {
      columnPinning[pinning[key]] = key;
    }
    const result = columnPinning.filter((item) => item);
    return sortType === 'ascend' ? result : result.reverse();
  };
  return { left: getPinning(left), right: getPinning(right, 'descend') };
}
// 固定列样式
export function getPinningStyle(params: {
  id: string;
  align: 'left' | 'right';
  pinning: string[];
  pinningInfo: { [key in string]: number };
  isShowBoxShadow?: boolean;
}) {
  const { isShowBoxShadow, id, align, pinning, pinningInfo } = params;
  const style: React.CSSProperties = {};
  const shadowInfo = {
    left: {
      index: pinning.length - 1,
      shadow: '1px 0 3px rgba(0, 0, 0, 0.15)',
    },
    right: { index: 0, shadow: '-1px 0 3px rgba(0, 0, 0, 0.15)' },
  };
  if (isShowBoxShadow && id === pinning[shadowInfo[align].index]) {
    style.boxShadow = shadowInfo[align].shadow;
  }
  style.position = 'sticky';
  style[align] = pinningInfo[id];
  style.zIndex = 10;

  return style;
}
export function getColumnPinningStyle(
  id: string,
  columnPinningInfo: ColumnPinningInfo,
  columnPinning: ColumnPinning,
  isShowBoxShadow?: boolean,
) {
  const style: React.CSSProperties = {};
  const { left, right } = columnPinningInfo;

  if (!isNullOrUndefined(left[id])) {
    return getPinningStyle({
      id,
      align: 'left',
      pinning: columnPinning.left,
      pinningInfo: left,
      isShowBoxShadow,
    });
  } else if (!isNullOrUndefined(right[id])) {
    return getPinningStyle({
      id,
      align: 'right',
      pinning: columnPinning.right,
      pinningInfo: right,
      isShowBoxShadow,
    });
  } else {
    return style;
  }
}

// 获取列计算公式信息
export function getColumnFormulaInfo<T extends AnyObject>(
  columns: ColumnDef<T>[],
) {
  const tableColumns = getAllFlatColumns(columns);
  const result: ColumnFormulaInfo<T> = {};

  tableColumns.forEach((column) => {
    const columnMeta = (column.meta as ColumnMeta<T>) || {};
    const { formula } = columnMeta;
    if (!!formula) {
      const id: keyof T = column.id;
      result[id] = formula;
    }
  });
  return result;
}
export function getColumnDataKeyInfo<T extends AnyObject>(
  columns: ColumnDef<T>[],
) {
  const tableColumns = getAllFlatColumns(columns);
  const result: ColumnDataKeyInfo<T> = {};
  tableColumns.forEach((column) => {
    const columnMeta = (column.meta as ColumnMeta<T>) || {};
    const { dataKey } = columnMeta;
    if (!!dataKey) {
      const id: keyof T = column.id;
      result[id] = dataKey;
    }
  });
  return result;
}
// 获取列的校验规则
export function getColumnRule<T extends AnyObject>(columns: T[]) {
  const tableColumns = getAllFlatColumns(columns);
  const result: Rules<T> = {};
  tableColumns.forEach((column) => {
    const columnMeta =
      (column.columnDef && column.columnDef.meta) ||
      (column.meta as ColumnMeta<T>) ||
      {};
    if (columnMeta.rule && columnMeta.rule.length) {
      result[column.id as keyof T] = columnMeta.rule;
    }
  });
  return result;
}
export function getColumnSize<T extends AnyObject>(columns: Column<T>[]) {
  const result: ColumnSizes<T> = Object.keys(defaultColumnConfig).reduce(
    (res, key) => {
      if (defaultColumnConfig[key].width) {
        res[key] = defaultColumnConfig[key].width;
      }
      return res;
    },
    {},
  );
  columns.forEach((column) => {
    const { size } = column;
    if (size) result[column.id as keyof T] = size;
  });
  return result;
}
export function getVisibleColumnSize<T extends AnyObject>(
  columns: Column<T>[],
  columnVisibility: SettingCheckBoxInfo,
) {
  const tableColumns = columns.filter((column) => columnVisibility[column.id]);
  return getColumnSize(tableColumns);
}
export function setColumnSize<T extends AnyObject>(
  columns: Column<T>[],
  columnSizes: ColumnSizes<T>,
  columnVisibility: SettingCheckBoxInfo,
  totalWidth: number,
) {
  const tableColumns = deepClone(columns);
  const defaultWidth = 150;
  const setSizecolumns = [];
  let remainingWidth = totalWidth;
  tableColumns.forEach((column) => {
    if (columnVisibility[column.id]) {
      const size = columnSizes[column.id];
      if (size) {
        remainingWidth -= size;
      } else {
        setSizecolumns.push(column);
      }
    }
  });
  const oneColumnWidth = Math.floor(remainingWidth / setSizecolumns.length);
  const columnWidth =
    oneColumnWidth < defaultWidth ? defaultWidth : oneColumnWidth;

  setSizecolumns.forEach((column) => {
    column.size = columnWidth;
  });

  return tableColumns;
}
